package com.radar.io.cinrad;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.radar.algorithm.MathCommon;
import com.radar.common.CinradType;
import com.radar.common.DateConstants;
import com.radar.common.GisConstants;
import com.radar.common.ProductType;
import com.radar.exception.RadarDecodeError;
import com.radar.utils.BitConverter;
import ucar.ma2.ArrayByte;
import ucar.ma2.ArrayDouble;
import ucar.ma2.DataType;
import ucar.nc2.Attribute;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;

import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.IntStream;

/**
 * @author: fy-wbj
 * @description: CC 波段 雷达
 * @date: 2020/10/10
 */
public class Radar720 extends BaseCinrad {

    private double[] ele;

    private double[][] azi;

    private double reso = 300;

    private Date scanEndTime;

    double maxL;

    static Log log = LogFactory.get();

    com.radar.common.RadarSite.RadarSiteItem radarSiteItem = null;

    public Radar720(String file, CinradType type, String code) {
        super(file, type, code);
    }

    public void parse(byte[] bytes) throws RadarDecodeError {
        RadarHeader header = new RadarHeader();
        int hLen = header.size;
        LineDataBlock tmp7 = new LineDataBlock();
        int bLen = tmp7.size;
        try {
            int index = 0;
            byte[] bytdata = new byte[hLen];
            System.arraycopy(bytes, index, bytdata, 0, bytdata.length);
            index += bytdata.length;
            header = new RadarHeader();
            header.parse(bytdata);
            GregorianCalendar gc = new GregorianCalendar();
            String format = header.radarObservaTionParam1.syear + "-" +
                    (header.radarObservaTionParam1.smonth < 10 ? "0" + header.radarObservaTionParam1.smonth : header.radarObservaTionParam1.smonth) + "-" +
                    (header.radarObservaTionParam1.sday < 10 ? "0" + header.radarObservaTionParam1.sday : header.radarObservaTionParam1.sday);
            Date date = DateUtil.parse(format, DateConstants.DATE_FORMAT_YY_MM_DD);
            gc.setTime(date);
            gc.set(GregorianCalendar.HOUR_OF_DAY, header.radarObservaTionParam1.shour);
            gc.set(GregorianCalendar.MINUTE, header.radarObservaTionParam1.sminute);
            gc.set(GregorianCalendar.SECOND, header.radarObservaTionParam1.ssecond);

            this.radarSiteItem = new com.radar.common.RadarSite().new RadarSiteItem(
                    header.radarSite.station.replaceAll("\u0000",""),
                    header.radarSite.stationnumber.replaceAll("\u0000",""),
                    header.radarSite.province.replaceAll("\u0000",""),
                    header.radarSite.station.replaceAll("\u0000",""),
                    CinradType.RADAR_720,
                    header.radarSite.longitudevalue / 1000.0,
                    header.radarSite.lantitudevalue / 1000.0,
                    header.radarSite.height / 100.0);

            scanEndTime = gc.getTime();
            this.dateTime = scanEndTime;
            int layers = -1;
            if (header.radarObservaTionParam1.stype > 100) {
                layers = header.radarObservaTionParam1.stype - 100;
                this.scanType = ScanType.VOL;
            } else {
                if (header.radarObservaTionParam1.stype == 10) {
                    layers = 1;
                    this.scanType = ScanType.PPI;
                } else {
                    if (header.radarObservaTionParam1.stype == 1)//RHI
                    {
                        layers = 1;
                        this.scanType = ScanType.RHI;
                    }
                }
            }
            double MaxV = header.layerParams[0].MaxV / 100.0;
            maxL = header.layerParams[0].MaxL * 10;
            reso = header.layerParams[0].ZBinWidth / 10.0;
            int[] records = new int[layers];
            for (int i = 0; i < layers; i++) {
                records[i] = header.layerParams[i].recordnumber;
            }
            index = 2060;
            int MaxRecord = Arrays.stream(records).max().getAsInt();
            LineDataBlock[][] data = new LineDataBlock[layers][MaxRecord];
            int datasize = new LineDataBlock().size;
            for (int i = 0; i < layers; i++) {
                for (int j = 0; j < records[i]; j++) {
                    byte[] tmp = new byte[datasize];
                    System.arraycopy(bytes, index, tmp, 0, tmp.length);
                    index += tmp.length;
                    data[i][j] = new LineDataBlock();
                    data[i][j].parse(tmp);
                }
            }
            int eBins = 1000;
            byte[][][] z = new byte[layers][MaxRecord][eBins];
            byte[][][] v = new byte[layers][MaxRecord][eBins];
            byte[][][] w = new byte[layers][MaxRecord][eBins];
            byte[][][] uz = new byte[layers][MaxRecord][eBins];
            ele = new double[layers];
            azi = new double[layers][MaxRecord];
            for (int i = 0; i < layers; i++) {
                MathCommon.fill(z[i]);
                MathCommon.fill(v[i]);
                MathCommon.fill(w[i]);
                MathCommon.fill(uz[i]);
                for (int j = 0; j < records[i]; j++) {
                    azi[i][j] = data[i][j].Azimuth / 100.0;
                    ele[i] = data[i][j].Elevation / 100.0;
                    for (int k = 0; k < eBins; k++) {
                        if (data[i][j].CorZ[k] != 0) {
                            double zv = (data[i][j].CorZ[k] - 64) / 2.0;
                            z[i][j][k] = MathCommon.CodeRef(zv);
                        }
                        if (data[i][j].UnZ[k] != 0) {
                            uz[i][j][k] = MathCommon.CodeRef((data[i][j].UnZ[k] - 64) / 2.0);
                        }
                        if (data[i][j].V[k] != -128) {
                            v[i][j][k] = MathCommon.CodeV((data[i][j].V[k] * MaxV / 127.0));
                        }
                        //TODO CODE V maybe error
                        if (data[i][j].W[k] != 0) {
                            w[i][j][k] = MathCommon.CodeV(MaxV * data[i][j].W[k] / 256.0);
                        }
                    }
                }
            }
            this.initNetcdfTemp();
            this.writeBasicData(z, v, w);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("锦江电子 720 RADAR handle error");
        }
    }

    @Override
    public double[] getElevation() {
        return ele;
    }

    @Override
    public int[] getElevationIndex(ProductType type) {
        return IntStream.range(0, ele.length).toArray();
    }

    @Override
    public com.radar.common.RadarSite.RadarSiteItem getRadarSite() throws RadarDecodeError {
        return this.radarSiteItem;
    }

    @Override
    public double[][] getAzimuth() {
        return azi;
    }

    @Override
    public double getResolution(ProductType type) {
        return reso;
    }

    public void writeBasicData(byte[][][] z, byte[][][] v, byte[][][] w) throws IOException {
        int dlength = z[0][0].length;
        write.setRedefineMode(true);
        Variable v1;
        if ((v1 = this.netcdf.findVariable("distince")) == null) {
            write.addDimension(null, "distince", dlength);
            v1 = write.addVariable(null, "distince", DataType.DOUBLE, "distince");
            v1.addAttribute(new Attribute("units", "m"));
            v1.addAttribute(new Attribute("resolution", reso));
        }

        List<Dimension> dimensions = new ArrayList<>();
        dimensions.add(netcdf.findDimension("elevation"));
        dimensions.add(netcdf.findDimension("azimuth"));
        dimensions.add(netcdf.findDimension("distince"));

        Variable rVariable = ProductType.REF.writeVariable(write, netcdf, dimensions);
        Variable vVariable = ProductType.VEL.writeVariable(write, netcdf, dimensions);
        Variable wVariable = ProductType.SW.writeVariable(write, netcdf, dimensions);

        ArrayDouble rDistances = new ArrayDouble.D1(dlength);
        IntStream.range(0, dlength).forEach(i -> rDistances.setDouble(i, (i + 1) * reso));
        v1.setCachedData(rDistances);

        ArrayByte rArray = MathCommon.toByteArray(z);
        ArrayByte wArray = MathCommon.toByteArray(w);
        ArrayByte vArray = MathCommon.toByteArray(v);
        rVariable.setCachedData(rArray);
        vVariable.setCachedData(vArray);
        wVariable.setCachedData(wArray);
    }

    //region 原始数据文件

    public class RadarHeader {

        RadarFileid radarFileid;
        RadarSite radarSite;
        RadarPerformanCeparam radarPerformanCeparam;
        RadarObservaTionParam1 radarObservaTionParam1;
        // 35
        LayerParam[] layerParams;
        RadarObservaTionParam12 radarObservaTionParam12;
        String Reserved;

        int size = 2039;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            radarFileid = new RadarFileid();
            radarFileid.parse(Arrays.copyOfRange(b, index, radarFileid.size + index));
            index += radarFileid.size;

            radarSite = new RadarSite();
            radarSite.parse(Arrays.copyOfRange(b, index, radarSite.size + index));
            index += radarSite.size;

            radarPerformanCeparam = new RadarPerformanCeparam();
            radarPerformanCeparam.parse(Arrays.copyOfRange(b, index, radarPerformanCeparam.size + index));
            index += radarPerformanCeparam.size;

            radarObservaTionParam1 = new RadarObservaTionParam1();
            radarObservaTionParam1.parse(Arrays.copyOfRange(b, index, radarObservaTionParam1.size + index));
            index += radarObservaTionParam1.size;

            layerParams = new LayerParam[35];
            for (int i = 0; i < 35; i++) {
                layerParams[i] = new LayerParam();
                layerParams[i].parse(Arrays.copyOfRange(b, index, layerParams[i].size + index));
                index += layerParams[i].size;
            }

            radarObservaTionParam12 = new RadarObservaTionParam12();
            radarObservaTionParam12.parse(Arrays.copyOfRange(b, index, radarObservaTionParam12.size + index));
            index += radarObservaTionParam12.size;
        }
    }

    public class RadarFileid {
        int size = 12;
        String FileID;
        float VersionNo;
        int FileHeaderLength;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            FileID = new String(Arrays.copyOfRange(b, index, index + 4));
            index += 4;
            VersionNo = BitConverter.toSingle(b, index);
            index += 4;
            FileHeaderLength = BitConverter.toInt32(b, index);
        }
    }

    /**
     * 雷达场地
     */
    public class RadarSite {
        int size = 168;
        String country;          //国家名，文本格式输入
        String province;         //省名，文本格式输入
        String station;          //站名，文本格式输入
        String stationnumber;    //区站号，文本格式输入
        String radartype;        //雷达型号，文本格式输入
        String longitude;        //天线所在经度，文本格式输入，书写格式：E115°32′12″
        String latitude;         //天线所在纬度，文本格式输入，书写格式：N35°30′15″
        int longitudevalue;    //天线所在经度的数值，以毫秒为计数单位(十进制)东经(E)为正，西经(W)为负
        int lantitudevalue;    //天线所在纬度的数值，以毫秒为计数单位(十进制)北纬(N)为正，南纬(S)为负
        int height;           //天线的海拔高度，以毫米为计数单位
        short maxangle;            //测站四周地物阻挡的最大仰角，以秒为计数单位
        short opangle;            //测站的最佳观测仰角(地物回波强度<10dbz,以秒为计数单位)

        public void parse(byte[] b) throws Exception {
            country = new String(Arrays.copyOfRange(b, 0, 30), "GBK");
            int index = 30;
            province = new String(Arrays.copyOfRange(b, index, 20 + index), "GBK");
            index += 20;
            station = new String(Arrays.copyOfRange(b, index, 40 + index), "GBK");
            index += 40;
            stationnumber = new String(Arrays.copyOfRange(b, index, 10 + index), "GBK");
            index += 10;
            radartype = new String(Arrays.copyOfRange(b, index, 20 + index), "GBK");
            index += 20;
            longitude = new String(Arrays.copyOfRange(b, index, 16 + index), "GBK");
            index += 16;
            latitude = new String(Arrays.copyOfRange(b, index, 16 + index), "GBK");
            index += 16;
            longitudevalue = BitConverter.toInt32(b, index);
            index += 4;
            lantitudevalue = BitConverter.toInt32(b, index);
            index += 4;
            height = BitConverter.toInt32(b, index);
            index += 4;
            maxangle = BitConverter.toInt16(b, index);
            index += 2;
            opangle = BitConverter.toInt16(b, index);
            index += 2;
        }
    }

    /**
     * 雷达性能参数
     */
    public class RadarPerformanCeparam {
        int antennaG;                //天线增益，以0.001db为计数单位
        int beamH;            //垂直波数宽度，以微秒为计数单位
        int beamL;            //水平波数宽度，以微秒为计数单位
        byte polarizations;     //极化状况 0=水平，  1=垂直， 2=双偏振，3=圆偏振，4=其他
        char sidelobe;              //第一旁瓣计数单位：db(注意：输入负号)
        int power;             //雷达脉冲峰值功率，以瓦为计数单位
        int wavelength;        //波长，以微米为计数单位
        int logA;       //对数接收机动态范围，以0.01db为计数单位
        int LineA;      //线性接收机动态范围，以0.01db为计数单位
        int AGCP;       //AGC延迟量，以微秒为计数单位
        byte clutterT;    //杂波消除阀值，计数单位0.01db
        byte velocityP;   //速度处理方式：0=无速度处理， 1=PPP， 2=FFT
        byte filderP;     /*地物消除方式：0=无地物消除， 1=地物杂波图扣除法 2=地物杂波图+滤波器处理 3=滤波器处理， 4=谱分析处理*/
        byte noiseT;     //噪声消除阀值(0-255)
        byte sQIT;       //SQI阀值，以0.01为计数单位
        byte intensityC;  //rvp强度值估算采用的通道：1=对数通道，2=线性通道
        byte intensityR;  //强度估算是否进行了距离订正 0=无， 1=已进行了距离订正
        byte[] Reserved1;
        int size = 36;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            antennaG = BitConverter.toInt32(b, index);
            index += 4;
            beamH = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            beamL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            polarizations = b[index];
            index++;
//            sidelobe = BitConverter.toString(Arrays.copyOfRange(b, index, 2 + index)).charAt(0);
            index++;
            power = BitConverter.toInt32(b, index);
            index += 4;
            wavelength = BitConverter.toInt32(b, index);
            index += 4;
            index += 2 * 3;
            clutterT = b[index];
            index += 1;
            velocityP = b[index];
            index += 1;
            filderP = b[index];
            index += 1;
            noiseT = b[index];
            index += 1;
            sQIT = b[index];
            index += 1;
            intensityC = b[index];
            index += 1;
            intensityR = b[index];
        }
    }

    public class RadarObservaTionParam1 {
        byte stype;       /*扫描方式：1=RHI   10=PPI   1XX=VOL XX为扫描圈数*/
        int syear;      //观测记录开始时间的年的十位个位(01-99)
        byte smonth;      //观测记录开始时间的月(1-12)
        byte sday;        //观测记录开始时间的日(1-31)
        byte shour;       //观测记录开始时间的时(00-23)
        byte sminute;     //观测记录开始时间的分(00-59)
        byte ssecond;     //观测记录开始时间的秒(00-59)
        byte Timep;       /*时间来源： 0=计算机时钟，但一天内未进行对时 2=GPS    3=其他*/
        int smillisecond;    //秒的小数位(记数单位微秒)
        byte calibration;         /*标校状态： 0=无标校  1=自动标校   2=1星期内人工标校 3=1月内人工标校    其他码不用*/
        byte intensityI;          //强度积分次数(32-128)
        byte VelocityP;           //速度处理样本数(31-255)(样本数-1)

        int ZStartBin;          //  强度有效数据开始库数   (232-233)
        int VStartBin;          //  速度有效数据开始库数   (234-235)
        int WStartBin;
        int size = 22;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            stype = b[index];
            index++;
            syear = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            smonth = b[index];
            index++;
            sday = b[index];
            index++;
            shour = b[index];
            index++;
            sminute = b[index];
            index++;
            ssecond = b[index];
            index++;
            Timep = b[index];
            index++;
            smillisecond = BitConverter.toInt32(b, index);
            index += 4;
            calibration = b[index];
            index++;
            intensityI = b[index];
            index++;
            VelocityP = b[index];
            index++;
            ZStartBin = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            VStartBin = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            WStartBin = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
        }
    }

    public class RadarObservaTionParam12 {
        int RHIA;    //作RHI时的所在方位角，计数单位为1/100度作PPI和立体扫描时为65535
        short RHIL;     //作RHI时的最低仰角，计数单位为1/100度作其他扫描时为-32768
        short RHIH;     //作RHI时的最高仰角，计数单位为1/100度作其他扫描时为-32768
        int Eyear;      //观测结束时间的年的十位个位(01-99)
        byte Emonth;      //观测结束时间的月(1-12)
        byte Eday;        //观测结束时间的日(1-31)
        byte Ehour;       //观测结束时间的时(00-23)
        byte Eminute;     //观测结束时间的分(00-59)
        byte Esecond;     //观测结束时间的秒(00-59)
        byte Etenth;      //观测结束时间的1/100秒(00-59)
        int size = 14;

        public void parse(byte[] b) throws Exception {
            int index = 0;
            RHIA = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            RHIL = BitConverter.toInt16(b, index);
            index += 2;
            RHIH = BitConverter.toInt16(b, index);
            index += 2;
            Eyear = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Emonth = b[index];
            index++;
            Eday = b[index];
            index++;
            Ehour = b[index];
            index++;
            Eminute = b[index];
            index++;
            Esecond = b[index];
            index++;
            Etenth = b[index];
        }
    }

    /**
     * 层参数
     */
    public class LayerParam {
        int size = 30 + 5;
        public byte DataType;
        public byte ambiguousp;    //本层退模糊状态
        public int Arotate;     //本层天线转速，计数单位：0.01度/秒
        public int Prf1;        //本层的第一种脉冲重复频率，计数单位：1/10Hz
        public int Prf2;        //本层的第二种脉冲重复频率，计数单位：1/10Hz
        public int spulseW;     //本层的脉冲宽度，计数单位：微秒
        public int MaxV;        //本层的最大可测速度，计数单位：厘米/秒
        public int MaxL;        //本层的最大可测距离，以10米为计数单位
        public int ZBinWidth;        //     本层强度数据的库长,计数单位为1/10米
        public int VBinWidth;        //     本层速度数据的库长,计数单位为1/10米
        public int WBinWidth;         //    本层谱宽数据的库长,计数单位为1/10米
        public int ZbinNumber;       //     本层扫描强度径向的库数
        public int VbinNumber;       //     本层扫描速度径向的库数
        public int WbinNumber;        //    本层扫描谱宽径向的库数
        public int recordnumber;  //本层扫描线垂直方向点数
        public int Swangles;              //本层的仰角，计数单位：1/100度
        public byte DataForm;                  /*    本层径向中的数据排列方式    11＝单要素CorZ    12＝单要素UnZ   13＝单要素V 14＝单要素W         21＝CorZ+UnZ            22＝CorZ+V+W23＝UnZ+V+W        24＝CorZ+UnZ+V+W4X双偏振按要素     6X-双偏振多谱勒按要素  8X-双波长按要素  */
        public int DBegin;             //    本层数据记录开始位置(字节数)

        public void parse(byte[] b) throws Exception {
            int index = 0;
            DataType = b[index];
            index++;
            ambiguousp = b[index];
            index++;
            Arotate = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Prf1 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Prf2 = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            spulseW = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxV = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            MaxL = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ZBinWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            VBinWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            WBinWidth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            ZbinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            VbinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            WbinNumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            recordnumber = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            Swangles = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            DataForm = b[index];
            index++;
            index += 4;
        }
    }

    public class LineDataBlock {
        int size = 4000 + 11;
        public short Elevation;
        public int Azimuth;
        public byte ucHour;
        public byte ucMinute;
        public byte ucSecond;
        public int ulMillisecond;
        public byte[] CorZ;
        public byte[] UnZ;
        public int[] V;
        public byte[] W;

        public void parse(byte[] b) {
            int index = 0;
            Elevation = BitConverter.toInt16(b, index);
            index += 2;
            Azimuth = (BitConverter.toInt16(b, index) & 0x0FFFF);
            index += 2;
            index += 3 + 4;
            CorZ = new byte[1000];
            System.arraycopy(b, index, CorZ, 0, CorZ.length);
            index += 1000;
            UnZ = new byte[1000];
            System.arraycopy(b, index, UnZ, 0, UnZ.length);
            index += 1000;
            V = new int[1000];
            byte[] bytdata = new byte[1000];
            System.arraycopy(b, index, bytdata, 0, bytdata.length);
            for (int i = 0; i < V.length; i++) {
                V[i] = (0xff & bytdata[i]);
            }
            index += 1000;
            W = new byte[1000];
            System.arraycopy(b, index, W, 0, W.length);
            index += 1000;
        }
    }

    //endregion


}
